require 'rubygems'
require 'mytree'
require 'Parser'
require 'supporting_functions'
require 'cleanup_js'

$my_const = {}
$consts.each {|k,v| $my_const[v] = k}

$valid_list_delimiters = {","=>'',"&&"=>'',"+"=>'', "-"=>'', "<<"=>'', ">>"=>'',"||"=>'',"|"=>'',"&"=>'',"%"=>'',"/"=>''}
class Value
  attr_accessor:value, :type
  def initialize(value=nil, type=nil)
    @type = $my_const[type] if type != nil
    @value = value
  end
  def to_s
      return @value.to_s
  end
end

class Identifier < Value
    attr_accessor :var_name, :info, :value, :name
    def initialize(name=nil, info=nil, type=nil)
        @var_name = name
        @name = @var_name
        @info = nil
        @type = $my_const[type] if type != nil
        @assignments = []
    end
    def update_value(value_instance)
        
        @value = value_instance.value if value_instance != nil and value_instance.value != nil
        @type = value_instance.type   if value_instance != nil and value_instance.type != nil  
    end
    def to_s
        if @value == nil
            return @name.to_s
        end      
        return (@name + " = "+ @value.to_s)
    end
end
class Index < Identifier
    attr_accessor :index, :name
    def initialize(name=nil)
        @var_name = name
        @name = @var_name
        @index = List.new("INDEX")
        @value = self
        @index.markers = ["[","]"]
    end
    def to_s
      return @name.to_s + @index.to_s
    end
end


class List
  attr_accessor :items, :type, :delimiter, :name, :identifiers, :var_name, :value, :markers
  def initialize(type=nil)
    @type = type
    @items = []
    @delimiter = ","
    @markers = ["",""]
    @identifiers = {}
    @value = self
  end
  def addItem(item)
    #balance list
    if item.class == self.class and @type == item.type
      if @type == "PLUS" or @type == "MINUS" or 
         @type == "MUL" or @type == "DIV"
         item.each { |i| @items << i }
      end
    else
      @items.push(item)
    end
  end
  def << (item)
    self.addItem(item)
  end
  def to_s
      if @delimiter != "," or @type != "OPERATION" 
          @delimiter = ","
      end
      return @markers[0] + @items.join(@delimiter) + @markers[1]
  end
  def join(delimiter)
    @items.join(delimiter)
  end
  def reduce_number
      if self.type == "OPERATION" and 
        self.delimiter == "-" or self.delimiter == "*" or self.delimiter == "+"
          numbers = []
          identifiers = []
          @items.each_index do |idx|
          if @items[idx].type == "NUMBER"
              numbers.push(idx)
          end
          num_end = numbers.size      
          start = 0     
          tracker = 0
          while start < num_end-1
            operand_1 = numbers[start] - tracker
            operand_2 = numbers[start+1]-tracker
            if @delimiter == "+"
              @items[operand_2].value = @items[operand_1].value + @items[operand_2].value
            elsif @delimiter == "-"
              @items[operand_2].value = @items[operand_1].value - @items[operand_2].value
            elsif @delimiter == "*"
              @items[operand_2].value = @items[operand_1].value * @items[operand_2].value
            end
            @items.delete_at(operand_1-tracker)
            tracker+=1
            start+= 1
          end
        end
      end
    end
  def reduce_string
    if self.type == "OPERATION" && self.delimiter == "+"
      strings = []
      @items.each_index do |idx|
        if @items[idx].type == "STRING"
          strings.push(idx)
        end
      end
      str_end = strings.size
      start = 0
      tracker = 0
      while start < str_end-1
        if strings[start]-tracker == strings[start+1]-1-tracker
          operand_1 = strings[start]-tracker
          operand_2 = strings[start+1]-tracker
          # trying to account for the \" in the string, but I am not sure this will work
          @items[operand_2].value = @items[operand_1].value[0..-2] + @items[operand_2].value[1..-1]
          @items.delete_at(start-tracker)
          tracker+=1
        end
        start+=1
      end
    end
  end
  def reduce
  #  Reduces list with constant values and if there is only 1 value in the list, 
  #  that value will be returned instead of the list 
      self.reduce_number
      self.reduce_string
      self.update_identifiers
  end
  def check
      self.reduce
      if self.type != "CALL_LIST" and self.items.size == 1 and 
        $valid_list_delimiters.has_key?(@delimiter)
          value = self.items[0]
          return value
      end
      return self
  end
  def update_identifiers
    @items.each_index do |ind| 
      if @items[ind].class == Identifier
        @identifiers[@items[ind].var_name] = ind
      end
    end
  end
  def to_s
    if @items.size > 1
      item_str = []
      @items.each {|item| item_str << item.to_s  }
      return (@markers[0] + @items.join(@delimiter) + @markers[1])
    end
    return (@markers[0] + @items[0].to_s + @markers[1])
  end
  def each 
    @items.each {|o| yield(o)}
    self
  end
end

class Function
  attr_accessor :name, :params, :vars, :lines, :name_info
  def initialize(name=nil, params=nil)
    if name.class == Identifier
       @name = name.get_name
       @name_info = name
    else
       @name = name
    end
    @params = params
    @name_info = nil
    @vars = {}
    @lines = []
  end
  def addVar(value)
    @vars[value.var_name] = value
  end
end

class Call
  attr_accessor :name, :value, :type, :list, :call_args, :var_name
  def initialize(name=nil)
    @name = name
    @type = type
    @value = self
    @call_args = nil
  end
  def <<(item)
    @call_args << item
  end
  def to_s
      return @name.to_s + @call_args.to_s    
  end
end

class Statement
  attr_accessor :name, :expression, :type, :list, :call_args, :modified_vars, :delimiter
  def initialize(type, value)
    @type = type
    @value = value
    @delimiter = ","
    @expression = []
  end
  def << (item)
    @expression << item
  end
  def to_s
      expr_str = @value+" "
      vals = []
      @expression.each{|item| vals << item.to_s}
      expr_str += (vals.join(@delimiter) + ";")
      return expr_str
  end
end

def parse_expression(node)
  if node.type == $consts["STRING"] or node.type == $consts["NUMBER"]
    return Value.new(node.value, node.type)
  elsif node.type == $consts["FUNCTION"]
    function = Function.new(node.name, node.params)
    bnodes = get_children(node.body)
    bnodes.each do |bnode|
        parsed_exp = parse_expression(bnode)
        if parsed_exp.class == List && parsed_exp.class == "VAR" 
          parsed_exp.each {|var| function.addVar(var)}
          function.lines << parse_exp
          print "This is a parsed exp", parsed_exp, "\n"
        end
        
        if bnode.type == $consts["FOR"]
        elsif bnode.type == $consts["TRY"]
        elsif bnode.type == $consts["IF"]
        elsif bnode.type == $consts["SWITCH"]
        elsif bnode.type == $consts["WHILE"]
        elsif bnode.type == $consts["DO"]
        else 
            function.lines << parse_expression(bnode)
        end
      end
      function.lines.each{|line| print line, "\n"}
      return function
  elsif node.type == $consts["RETURN"]
      enodes = get_children(node)
      stmt = Statement.new(node.type, "return")
      enodes.each do |enode|
          stmt << parse_expression(enode)
      end
      return stmt 
  elsif node.type == $consts["PLUS"] or node.type == $consts["MINUS"]
      
      pnodes = get_children(node)
      list = List.new("OPERATION")
      list.delimiter = node.value
      pnodes.each do |pnode|
          list << parse_expression(pnode)
      end
      list = list.check
      return list
  elsif node.type == $consts["VAR"]
    stmt = Statement.new(node.type, node.value)
    list = List.new("VAR")
    list.delimiter= ","
    vnodes = get_children(node)
    vnodes.each do |vnode|
        identifier = parse_expression(vnode)
        value = parse_expression(vnode.initializer)
        identifier.update_value(value)
        stmt << identifier
        #add line of code to the function here
    end
    return stmt
  elsif node.type == $consts["DOT"] or node.type == $consts["IDENTIFIER"]
      return Identifier.new(resolve_name(node))
  elsif node.type == $consts["LIST"]
      c_nodes = get_children(node)
      list = List.new(node.type)
    
      if $valid_list_delimiters.has_key?(node.value)
          list.delimiter = node.value  
      end
      list.delimiter = node.value
      c_nodes.each do |c_node|
          list << parse_expression(c_node)
      end
      return list.check
  elsif node.type == $consts["CALL"]
      cnodes = get_children(node)
      
      name = parse_expression(cnodes[0])
      caller = Call.new(name.to_s)
      cnodes[1..-1].each do |cnode|
          caller.call_args = parse_expression(cnode)
      end
      caller.call_args.type = "CALL_LIST"
      caller.call_args.reduce()
      caller.call_args.markers = ["(",")"]
      return caller
  elsif node.type == $consts["INDEX"]
      cnodes = get_children(node)
      index = Index.new(parse_expression(cnodes[0]))
      cnodes[1..-1].each do |cnode|
          index.index << parse_expression(cnode)
      end
      return index
  else
      print "Processing may not exist for Type = #{$my_const[node.type]}.\n"
  end
end


    

def scan_for_variables(cfg_tree)
  functions_table = Hash.new() # this will asctually be a per function table of variables
  functions = Hash.new()
  cfg_tree.each do |node|
      if node.type == $consts["FUNCTION"]
        functions[node.name] = node
      end
  end
  functions.each do |function|
      bnodes = get_children(function)
      bnodes.each do |node|
          if node.type == $consts["TYPEOF"] and node.value == "var"
            # parse identifiers, and obtain their corresponding assignments if they occur
          elsif node.type == $consts["ASSIGN"]
            # add the assignment to the identifier
          end
          
      end    
  end
end  
filename = "../index.php"

mytree = MyTree.new()
mytree.parse_file(filename)
nodes = mytree.find_all_node_tag("script")

new_codes = []
nodes.each do |node|
  new_codes << node.data
end
nc = new_codes.join("\n")
jstree = parse(nc, "", 0)
children = get_children(jstree)
j = children[0]
f = parse_expression(j)
